/* Name: Planet
 * Author: John Gideon
 * Date: 5-2-12
 * Description: All planets must be conquered in order for the game to be won. They each can hold a finite amount
 *  of ore and fuel and can relay the resources to other Planets through Wormholes. A Planet can only be controlled
 *  if it is connected to Earth through Wormholes. Planets can be destroyed by supernova and their resources can be
 *  depleted by a GammaRayBurst. Can build Wormholes and deploy probes.
 * Revisions: See Subversion logs at http://code.google.com/p/software-engineering-2012/source/list
 */

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
import org.jgrapht.alg.ConnectivityInspector;
import org.jgrapht.alg.KruskalMinimumSpanningTree;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleWeightedGraph;

public class Planet extends Button {
	private static LinkedList<Planet> idList = new LinkedList<Planet>();
	final private static int SPRITE_RADIUS = 98;
	private float scale;
	private Sprite sprite;
	public static Boolean addPlanet = false;
	private float rot = 0;
	private float rotSpeed = 0;
	private static String planetNames[];
	private static int nameOn = 0;
	private String name;
	private int disFromEarth;
	private LinkedList<Wormhole> whList = new LinkedList<Wormhole>();
	public static int numPlanets = 0;
	public static int numConquered = 0;
	public Boolean active = true;
	public float maxOre = 0;
	public float maxFuel = 0;
	public float ore = 0;
	public float fuel = 0;
	
	// Places the Planet randomly in the room, preventing collisions and impossible scenarios. Initializes graphics.
	public Planet(int pDisFromEarth) {
		super(0,0,0); // Temporarily set button size
		active = false;
		Game.create((Item)this, 15); // Needed for update, draw, and destroy
		colType = COL_CIRCLE; // Needed for collision checking
		idList.add(this); // Needed for collision checking
		disFromEarth = pDisFromEarth;
		if (disFromEarth == 0) {
			scale = 1.0f;
			x = Game.randomInt(r+550,GameScreen.getWidth()-r-550);
			y = Game.randomInt(r+550,GameScreen.getHeight()-r-550);
		} else {
			int attempt = 0;
			do {
				scale = Game.randomFloat(0.5f) + 0.5f;
				r = (int)(SPRITE_RADIUS*scale*3.0f);
				x = Game.randomInt(r,GameScreen.getWidth()-r);
				y = Game.randomInt(r,GameScreen.getHeight()-r);
			} while (Planet.checkCollision(this, false)!=0 || Nebula.checkCollision(this, false)!=0 || 
					AsteroidField.checkCollision(this, false)!=0 || Antimatter.checkCollision(this, false)!=0 ||
					Wormhole.checkCollision(this, false) != 0 || !GameScreen.isGameWinnable() || ++attempt >= 30);
			if (attempt >= 30) {
				GameScreen.throwRoomCreationError();
			}
		}
		r = (int) (SPRITE_RADIUS*scale);
		rot = Game.randomFloat((float) (2.0f*Math.PI));
		if (Game.randomInt(2) == 0) {
			rotSpeed = (float) (Game.randomFloat((float) (2.0f*Math.PI/10000.0f), (float) (2.0f*Math.PI/5000.0f)));
		} else {
			rotSpeed = (float) (Game.randomFloat((float) (-2.0f*Math.PI/5000.0f), (float) (-2.0f*Math.PI/10000.0f)));
		}
		if (disFromEarth == 0) {
			sprite = Sprite.load("/sprites/planets/earth.png");
			name = "EARTH";
			ore = maxOre = 10f;
			fuel = maxFuel = 15f;
			numConquered++;
		} else {
			String tmpStr = String.format("/sprites/planets/planet%d.png", Game.randomInt(17)+1);
			sprite = Sprite.load(tmpStr);
			name = "???";
			maxOre = Game.randomFloat(5,10f);
			maxFuel = Game.randomFloat(8,15f);
			ore = Game.randomFloat(maxOre/4.0f);
			fuel = Game.randomFloat(maxFuel);
		}
		numPlanets++;
	}
	
	// Called every frame: Updates button status, resources, and graphics rotation
	public void update() {
		updateBtn();
		GameScreen.totalOre = CalculateTotalOre();
		GameScreen.totalFuel = CalculateTotalFuel();
		if (selected && (disFromEarth < 0 || !active)) {
			GameScreen.setSelectedPlanet(null);
			selected = false;
			GameScreen.setButtonsVisible(false);
			Wormhole.deleteAllTemp();
		}
		
		// Draw Update
		rot += rotSpeed;
	}
	
	// Makes colliding Wormholes invalid, deletes self if collision with Supernova, and removes resources if GammaRayBurst collision
	public void collision(Item other) {
		if (other instanceof Wormhole){
			Wormhole cOther = (Wormhole)other;
			if(cOther.x != this.x & cOther.y != this.y & Math.sqrt(Math.pow(cOther.x2-this.x,2)+Math.pow(cOther.y2-this.y,2)) > r){ // if the starting end of the wormhole is not connected
				cOther.setPlacementValidity(false);		 																			// to this planet
			}
		}
		else if(other instanceof Supernova){
			while(this.whList.size() > 0){
				Wormhole tempHole = this.whList.remove();
				Game.delete(tempHole);
			}
			Game.delete(this);
			Wormhole.deleteAllTemp();
			if (Planet.numConquered == Planet.numPlanets) {
				GameScreen.winGame();
			}
		}
		else if(other instanceof GammaRayBurst){
			if(this.active && this.disFromEarth >= 0){
				this.fuel = 0;
				this.ore = 0;
			}
		}
	}
	
	// Plays sound and sets to selected if conquered
	protected void pressed() {
		if (disFromEarth >= 0 && active) {
			Sound.effect(Sound.EFFECT_CLICK);
			Planet.deselectAll();
			GameScreen.setSelectedPlanet(this);
			selected = true;
			GameScreen.setButtonsVisible(true);
		} else {
			setSelected(false);
		}
	}
	
	// Deselects Planet
	protected void released() {
		if (disFromEarth >= 0) {
			GameScreen.setSelectedPlanet(null);
			selected = false;
			GameScreen.setButtonsVisible(false);
		} else {
			setSelected(false);
		}
	}
	
	// Forwards mouse press to inherited Button class
	public void leftMousePress(){
		leftMousePressBtn();
	}
	
	// Forwards mouse release to inherited Button class
	public void leftMouseRelease(){
		leftMouseReleaseBtn();
	}
	
	// Called when destroyed: removes self from Planet list and decreases associated counters
	public void destroy() {
		idList.remove(this); // Needed for collision checking
		numPlanets--;
		if (disFromEarth >= 0) {
			numConquered--;
		}
	}
	
	// Called every frame: Draws associated sprite and information
	public void draw() {
		if (onScreen()) {
			Graphics2D gh = Game.getGraphicsHandle();
			sprite.draw(gh, x, y, rot, scale);
			
			if (btnDrawState == Button.BTN_STATE_OFF) {gh.setColor(Color.DARK_GRAY);}
			else if (btnDrawState == Button.BTN_STATE_HOVER) {gh.setColor(Color.LIGHT_GRAY);}
			else {gh.setColor(Color.YELLOW);}
			if(!this.active){gh.setColor(Color.RED);}
			if(disFromEarth == -1){gh.setColor(Color.BLACK);}
			gh.setStroke(new BasicStroke(5.0f));
			gh.drawOval(x-r+1, y-r+1, (r-1)*2, (r-1)*2);
			gh.setStroke(new BasicStroke(1.0f));
			
			gh.setFont(new Font("Serif", Font.PLAIN, 20));
			FontRenderContext frc = gh.getFontRenderContext();
			Rectangle2D rect = gh.getFont().getStringBounds(name, frc);
			int xPos = (int) (x - rect.getWidth()/2);
			int yPos = (int) (y + rect.getHeight()/2);
			gh.setColor(new Color(0.0f,0.0f,0.0f,0.60f));
			gh.fillRoundRect(xPos - 3, yPos - 3 - (int)rect.getHeight(), (int)rect.getWidth()+6, (int)rect.getHeight()+6, 8, 8);
			if (disFromEarth == 0) {
				gh.setColor(Color.GREEN);
			} else {
				gh.setColor(Color.WHITE);
			}
			//name = String.format("Dis: %d", disFromEarth);
			gh.drawChars(name.toCharArray(), 0, name.length(), xPos, yPos-3);
		}
	}
	
	// Deselects all Planets (usually before selecting another)
	private static void deselectAll() {
		GameScreen.setSelectedPlanet(null);
		for (int i = 0; i < idList.size(); i++) {
			Planet idOn = idList.get(i);
			idOn.selected = false;
		}
	}
	
	// Function needed in each item class for collision checking
	public static int checkCollision(Item other, Boolean notify) {
		int num = 0;
	    for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Item idOn = (Item)it.next();
			if (idOn != other) {
				if (Item.intersection(other, idOn)) {
					if (notify) {
						idOn.collision(other);
					}
					num++;
				}
			}
		}
		return num;
	}
	
	// Load the random Planet names from the external text file
	public static void initNames() {
		FileInputStream fstream = null;
		try {
			fstream = new FileInputStream("planetNames.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		planetNames = new String[200];
	    try {
	    	for (int i = 0; i < 200; i++) {
	    		planetNames[i] = br.readLine();
	    	}			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	// Randomize the order of the Planet names
	public static void resetNames() {
		nameOn = 0;
		// Randomize order - http://en.wikipedia.org/wiki/Knuth_shuffle#The_modern_algorithm
		for (int i = 199; i > 0; i--) {
			int randIndex = Game.randomInt(i+1);
			String tmp = planetNames[i];
			planetNames[i] = planetNames[randIndex];
			planetNames[randIndex] = tmp;
		}
	}
	
	// Get the next name from the random list
	private static String getNextName() {
		return planetNames[nameOn++];
	}
	
	// Returns the Item on the other end of the Wormhole currently being constructed if Planet
	public static Item getWormholeEnd(){
		Item returnItem = null;
		for(int count=0;count<idList.size();count++){
			Item tempItem = idList.get(count);
			if (Math.sqrt(Math.pow(tempItem.x-ViewControl.mouseX(),2)+Math.pow(tempItem.y-ViewControl.mouseY(),2)) < tempItem.r){
				returnItem = idList.get(count);
				break;
			}
			
		}
		return returnItem;
	}
	
	// Called when connected with wormhole, sets new name, and checks win condition
	public void conquer() {
		if (disFromEarth == -1) {
			disFromEarth = 1;
			name = getNextName();
			active = true;
			Fog.uncover(x, y, 300);
			numConquered++;
		}
		if (Planet.numConquered == Planet.numPlanets) {
			GameScreen.winGame();
		}
	}
	
	// Adds a new Wormhole to the list
	public void addWormhole(Wormhole wid) {
		whList.add(wid);
	}
	
	// Removes a Wormhole from the list
	public void removeWormhole(Wormhole wid){
		for(int count=0; count < whList.size(); count++){
			Wormhole nextHole = (Wormhole)whList.get(count);
			if(nextHole == wid){
				whList.remove(count);
				break;
			}
		}
	}
	
	// Determines and sets all Planet distances from Earth
	public static void calculateDisFromEarth() {
	    for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Planet idOn = (Planet) it.next();
	    	if (idOn.disFromEarth > 0) {
	    		idOn.disFromEarth = -2;
	    	}
		}
	    for (int levelOn = 0; calculateDisFromEarthSub(levelOn); levelOn++){}
	    Wormhole.determineDirections();
	}
	
	// Used as a recursive function for calculating distance from Earth
	private static Boolean calculateDisFromEarthSub(int level) {
	    Boolean nextLevel = false;
		for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Planet idOn = (Planet) it.next();
	    	if (idOn.disFromEarth == level) {
	    		for(Iterator it2 = idOn.whList.iterator(); it2.hasNext();) {
	    			Wormhole wid = (Wormhole) it2.next();
	    			Planet other = wid.getOtherPlanet(idOn);
	    			if (other != null) {
		    			if (other.disFromEarth == -2) {
		    				other.disFromEarth = level+1;
		    				nextLevel = true;
		    			}
	    			}
	    		}
	    	}
		}
	    return nextLevel;
	}
	
	// called every second to move resources around
	public void secondPulse(){
	    for (int levelOn = 1; moveResources(levelOn); levelOn++){}; // nothing is being pushed from Earth (level = 0), so start at level 1
	}
	
	// function to move ore and fuel from one planet to others connected to it that are the same distance from, or closer, to earth
	private static Boolean moveResources(int level){
		Boolean nextLevel = false; // assume no planets exist in the current level
		for(Iterator it = idList.iterator(); it.hasNext();){ // cycle through the list of planets
			Planet idOn = (Planet) it.next();
			if(idOn.disFromEarth != level || !idOn.active){ // if the planet is not at the specified level or is not currently
				continue;									// active, move no resources and continue cycling through planets
			}
			else{
				nextLevel = true; // at least one planet exists at the current level, so next level will be searched
				float oreDistribution = 0; // amount of ore to be pushed to each planet
				float fuelDistribution = 0;// amount of fuel to be pushed to each planet
				int lowerPlanetCountOre = 0; // planets <= level that can receive ore
				int lowerPlanetCountFuel = 0;// planets <= level that can receive fuel
				LinkedList<Planet> connectedPlanet = new LinkedList<Planet>();
				for(Iterator it2 = idOn.whList.iterator(); it2.hasNext();){ // cycle through the wormholes connected to the current planet
					Wormhole tempWormhole = (Wormhole)it2.next();
					Planet tempPlanet = tempWormhole.getOtherPlanet(idOn); // retrieve handle of item at other end of wormhole
					if (tempPlanet == null){ // the item is a nebula/asteroid field, so nothing is pushed to it
						continue;
					}
					if(!tempWormhole.active){ // if the wormhole connecting the planets is inactive, no resources
						continue;  			  // can be pushed through it, so skip it.
					}
					if (tempPlanet.disFromEarth <= idOn.disFromEarth){ // ensure the other planet is <= level
						connectedPlanet.add(tempPlanet); // add the planet to list of planets for which resources will be pushed
						if (tempPlanet.maxFuel > tempPlanet.fuel){ // check if fuel can be pushed to the planet
							lowerPlanetCountFuel++;  // if the planet has room for more fuel, increase this count
						}
						if(tempPlanet.maxOre > tempPlanet.ore){ // check if ore can be pushed to the planet
							lowerPlanetCountOre++; // if the planet has room for more ore, increase this count
						}
					}
				}
				float oreForDistribution = 0; // total ore to be distributed
				float fuelForDistribution = 0;// total fuel to be distributed
				if(lowerPlanetCountOre > 0 && idOn.ore >= 1f){ // maximum amount of ore that can be pushed is 1, see if planet has enough ore
					oreDistribution = 1f/(float)lowerPlanetCountOre; // to support this. It it does, divide by number of planets that will 
					idOn.ore--;									     // receive ore to get distribution and decrement total ore for this planet
					oreForDistribution = 1f; // keep track of amount of ore left to be distributed
				}
				else if(lowerPlanetCountOre > 0 && idOn.ore > 0){ // planet does not have enough ore to push 1 unit, so see if there is any ore
					oreDistribution = idOn.ore/(float)lowerPlanetCountOre; // to push. If there is, do same process as above, but with whatever
					oreForDistribution = idOn.ore;							// ore is left for the planet
					idOn.ore = 0;   // planet is pushing the last of its ore
				}
				// perform same process above for fuel as was done for ore
				if(lowerPlanetCountFuel > 0 && idOn.fuel >= 1f){
					fuelDistribution = 1f/(float)lowerPlanetCountFuel;
					idOn.fuel--;
				}
				else if(lowerPlanetCountFuel > 0 && idOn.fuel > 0){
					fuelDistribution = idOn.fuel/(float)lowerPlanetCountFuel;
					oreForDistribution = idOn.fuel;
					idOn.fuel = 0;
				}
				for(Iterator it3 = connectedPlanet.iterator(); it3.hasNext();){
					Planet tempPlanet = (Planet)it3.next();
					// Distribute the fuel
					Boolean allDistributed = false;  
	    			Boolean allFull = false;
	    			float fuelLeft = fuelForDistribution; // amount of fuel left for distribution
	    			while(!allDistributed && !allFull){ // loop through while all fuel has not been distributed and the connected planets are not
	    				allFull = true;					// at maximum capacity for fuel. Assume that the total amount of fuel will be distributed
	    				allDistributed = true;			// this cycle and that all planets will fill up to max amount of fuel.
		    			for(Iterator it4 = connectedPlanet.iterator(); it4.hasNext();){
		    				Planet pushPlanet = (Planet)it4.next(); // get handle of a planet to which fuel will be distributed
		    				if(fuelLeft == 0){ // if there is no fuel left to distribute, break out of the loop
		    					allDistributed = true;
		    					break;
		    				}
		    				else if(pushPlanet.fuel + fuelDistribution <= pushPlanet.maxFuel){ // find out if pushing max amount of fuelDistribution
		    					pushPlanet.fuel += fuelDistribution;  // will increase the planets fuel amount over its maximum amount. If it will not,
		    					fuelLeft -= fuelDistribution;		  // push total amount of fuelDistribution
		    					allFull = false;	    // Because fuel can be pushed ot at least one planet, assume another cycle is needed to 
		    					allDistributed = false; // distribute rest of fuel and that the planets are not yet filled to capacity
		    				}
		    				else if(pushPlanet.fuel >= pushPlanet.maxFuel){ // planet is filled to maximum capacity for fuel, so skip it
		    					continue;
		    				}
		    				else if(pushPlanet.fuel + fuelDistribution > pushPlanet.maxFuel){ // if fuel can be pushed to the planet, but not the amount
		    					float tempFuel = (pushPlanet.fuel + fuelDistribution)-pushPlanet.maxFuel; // specified by fuel distribution, push the amount
		    					pushPlanet.fuel = pushPlanet.maxFuel;		// that will completely fill up the fuel for the planet
		    					fuelLeft -= tempFuel;
		    					allFull = false;   // since fuel could be pushed to this planet, assume another cycle is needed to fill the other planets
		    				}
		    				if(fuelLeft <= 0){ // check to see if there is any more fuel left to distribute. If there is, cycle one more time so long
		    					allDistributed = true;	// as the other connected planets are not filled up.
		    					break;
		    				}
		    			}
		    			if(fuelDistribution <= 0){ // if fuelDistribution is zero, there's nothing to distribute, so break out of the loop
		    				break;
		    			}
	    			}
	    			
	    			// Distribute the ore, following the same process for fuel. Code is exactly the same, except "fuel" is replaced with "ore"
	    			allDistributed = false;
	    			allFull = false;
	    			float oreLeft = oreForDistribution;
	    			while(!allDistributed && !allFull){
	    				allFull = true;
	    				allDistributed = true;
		    			for(Iterator it5 = connectedPlanet.iterator(); it5.hasNext();){
		    				Planet pushPlanet = (Planet)it5.next();
		    				if(oreLeft == 0){
		    					allDistributed = true;
		    					break;
		    				}
		    				else if(pushPlanet.ore + oreDistribution <= pushPlanet.maxOre){
		    					pushPlanet.ore += oreDistribution;
		    					oreLeft -= oreDistribution;
		    					allFull = false;
		    					allDistributed = false;
		    				}
		    				else if(pushPlanet.ore >= pushPlanet.maxOre){
		    					continue;
		    				}
		    				else if(pushPlanet.ore + oreDistribution > pushPlanet.maxOre){
		    					float tempOre = (pushPlanet.ore + oreDistribution)-pushPlanet.maxOre;
		    					pushPlanet.ore = pushPlanet.maxOre;
		    					oreLeft -= tempOre;
		    					allFull = false;
		    				}
		    				if(oreLeft <= 0){
		    					allDistributed = true;
		    				}
		    			}
		    			if(oreDistribution <= 0){ // if oreDistribution is zero, there's nothing to distribute, so break out of the loop
		    				break;
		    			}
	    			}
				}					
			}
		}
		
		return nextLevel; // tell secondPulse to other continue to the next level or not
	}
	
	// Sum up all the ore held by the planets. A planet's ore contributes to the total amount if it is active and it has some
	// link to Earth
	public static float CalculateTotalOre(){
		float totalOre = 0;
		for(Iterator<Planet> it = idList.iterator(); it.hasNext();){
			Planet tempPlanet = (Planet)it.next();
			if(tempPlanet.disFromEarth >= 0 && tempPlanet.active){
				totalOre += tempPlanet.ore;
			}
		}
		return totalOre;
	}
	// Sum up all the fuel held by the planets. A planet's fuel contributes to the total amount if it is active and it has some
	// link to Earth
	public static float CalculateTotalFuel(){
		float totalFuel = 0;
		for(Iterator<Planet> it = idList.iterator(); it.hasNext();){
			Planet tempPlanet = (Planet)it.next();
			if(tempPlanet.disFromEarth >= 0 && tempPlanet.active){
				totalFuel += tempPlanet.fuel;
			}
		}
		return totalFuel;
	}
	
	// High level function that determines if a planet has some active link to Earth
	public static void SetPlanetActivity(){
		int levelOn = 1; // start at level 1 because Earth is always active
		for (levelOn = 1; PlanetActivity(levelOn); levelOn++){}; 
		int reverse = levelOn;
		for (levelOn = reverse; levelOn > 0; levelOn --){ // now do the process in reverse to ensure planets are made active if there is a link
			ReversePlanetActivity(levelOn); // to Earth through a planet at a higher level
		}
		
	}
	
	// Determines the activity level of all Planets based on Wormhole connections
	private static Boolean PlanetActivity(int level){
		Boolean nextLevel = false; // assume no planets exist at this level (and by consequence, any higher level)
		for(Iterator<Planet> it = idList.iterator(); it.hasNext();){ // cycle through the list of planets and find 
			Planet tempPlanet = (Planet)it.next();				// any that exist at the level specified
			if(tempPlanet.disFromEarth != level){
				continue;
			}
			nextLevel = true; // at least one planet exists at this level, so assume at least one exists at a higher level
			Boolean isActive = false; // assume this planet is not going to be considered active
			
			// now cycle through the list of wormholes connected to this planet
			for(Iterator<Wormhole> it2 = tempPlanet.whList.iterator(); it2.hasNext();){
				Wormhole currentHole = (Wormhole)it2.next();
				Planet otherPlanet = currentHole.getOtherPlanet(tempPlanet); // get handle of item at other end of wormhole
				if(otherPlanet != null){ // if the item is a planet...
					if(otherPlanet.disFromEarth < tempPlanet.disFromEarth){ // determine if the other planet is at a lower level than this planet...
						// if it is, consider this planet active iff the connecting wormhole is active and not colliding with a random event
						if(Blackhole.checkCollision(currentHole, true) == 0 && GammaRayBurst.checkCollision(currentHole, true) == 0 && currentHole.active && !currentHole.forceOff){
							isActive = true;
						}
					}
					// if the other planet is at the same level as this planet...
					else if(otherPlanet.disFromEarth == tempPlanet.disFromEarth){
						// consider this planet active if the connecting wormhole is active, not colliding with a random event, and the other planet is considered active
						if(checkOtherPlanet(otherPlanet) && Blackhole.checkCollision(currentHole, true) == 0 && GammaRayBurst.checkCollision(currentHole, true) == 0 && !currentHole.forceOff){
							isActive = true;
						}
					}
				}
			}
			// if this planet is not considered active, deactive it and all wormholes from it that connect to planets at level >= the level of this planet
			if(!isActive){
				tempPlanet.active = false;
				for(Iterator<Wormhole> it3 = tempPlanet.whList.iterator(); it3.hasNext();){
					Wormhole nextHole = (Wormhole)it3.next();
					Planet nextPlanet = (Planet)nextHole.getOtherPlanet(tempPlanet);
					if(nextPlanet == null){
						nextHole.active = false;
					}
					else if(nextPlanet.disFromEarth >= tempPlanet.disFromEarth){
						nextHole.active = false;
					}
				}
			}
			// if this planet is considered active, activate it and possibly active all connecting wormholes and planets
			else{
				tempPlanet.active = true;
				for(Iterator<Wormhole> it3 = tempPlanet.whList.iterator(); it3.hasNext();){
					Wormhole nextHole = (Wormhole)it3.next();
					Planet nextPlanet = (Planet)nextHole.getOtherPlanet(tempPlanet);
					if(nextPlanet == null){
						if(Blackhole.checkCollision(nextHole, true) == 0 && GammaRayBurst.checkCollision(nextHole, true) == 0){
							// if the connecting wormhole is not being forced to an inactive state, activate it
							if(!nextHole.forceOff){
								nextHole.active = true;
							}
						}
					}
					else if(nextPlanet.disFromEarth >= tempPlanet.disFromEarth){
						if(Blackhole.checkCollision(nextHole, true) == 0 && GammaRayBurst.checkCollision(nextHole, true) == 0){
							// if the connecting wormhole is not being forced to an inactive state, activate it
							if(!nextHole.forceOff){
								nextHole.active = true;
							}
							// if the connected planet is at a level >= the level of this planet..
							if(nextPlanet.disFromEarth >= tempPlanet.disFromEarth){
								// active that connecting planet only if the connecting wormhole is active
								if(tempPlanet.active && nextHole.active){
									nextPlanet.active = true;
								}
							}
						}
					}
				}
			}
			
		}
		return nextLevel;
	}
	
	// This function is used by PlanetActivity for the case where two planets reside at the same level. It cycles through wormholes connecting
	// a planet to planets at a level <= the planet in question. If any of the wormholes are active, the planet is considered active.
	private static Boolean checkOtherPlanet(Planet other){
		Boolean isActive = false;
		for(Iterator it = other.whList.iterator(); it.hasNext();){
			Wormhole hole = (Wormhole)it.next();
			Planet otherPlanet = hole.getOtherPlanet(other);
			if(otherPlanet != null){
				if(otherPlanet.disFromEarth < other.disFromEarth){
					if(hole.active && Blackhole.checkCollision(hole, true) == 0 && GammaRayBurst.checkCollision(hole, true) == 0){
						isActive = true;
					}
				}
			}
		}
		return isActive;
	}
	
	// Performs nearly same function as PlanetActivity, but starts with planets at the highest level. Because is has already gone from 
	// center outward, this function merely activates all planets and wormholes connected to a planet at a higher level, if the higher-leveled
	// planet is active
	private static void ReversePlanetActivity(int level){
		for(Iterator<Planet> it = idList.iterator(); it.hasNext();){
			Planet tempPlanet = (Planet)it.next();
			if(tempPlanet.disFromEarth != level || !tempPlanet.active){
				continue;
			}
			for(Iterator<Wormhole> it2 = tempPlanet.whList.iterator(); it2.hasNext();){
				Wormhole nextHole = (Wormhole)it2.next();
				Planet nextPlanet = (Planet)nextHole.getOtherPlanet(tempPlanet);
				if(nextPlanet != null){
					// activate the wormhole and planet only if the wormhole is not colliding with a random event and is not being forced inactive
					// by the user
					if(!nextHole.forceOff && Blackhole.checkCollision(nextHole, true) == 0 && GammaRayBurst.checkCollision(nextHole, true) == 0){
						if(nextPlanet.disFromEarth < tempPlanet.disFromEarth){
							nextHole.active = true;
							nextPlanet.active = true;
						}
					}
				}
			}
		}
	}
	
	// Removes an amount of ore from the total system, spreading the cost between all planets
	public static void removeOre(float removeAmount){
		int activeCount = 0;
		LinkedList<Planet> planetList = new LinkedList<Planet>();
		for(Iterator it = idList.iterator(); it.hasNext();){
			Planet thisPlanet = (Planet)it.next();
			if(thisPlanet.active){
				activeCount++;
				planetList.add(thisPlanet);
			}
		}
		float oreLeft = removeAmount;
		Boolean allRemoved = false;
		float removeDist = 0;
		if(activeCount > 0){
			removeDist = removeAmount/((float)activeCount);
		}
		else{
			allRemoved = true;
		}		
		while(!allRemoved){
			for(Iterator it2 = planetList.iterator();it2.hasNext();){
				Planet nextPlanet = (Planet)it2.next();
				if(nextPlanet.ore >= removeDist){
					nextPlanet.ore -= removeDist;
					oreLeft -= removeDist;
				}
				else if(nextPlanet.ore > 0){
					float tempOre = nextPlanet.ore;
					nextPlanet.ore = 0;
					oreLeft -= tempOre;
				}
				if(oreLeft <= 0){
					allRemoved = true;
					break;
				}
			}
		}
	}
	
	// Adds an amount of ore to the total system, spreading the gain between all planets
	public static void addOre(float addAmount){
		int activeCount = 0;
		LinkedList<Planet> planetList = new LinkedList<Planet>();
		for(Iterator it = idList.iterator(); it.hasNext();){
			Planet thisPlanet = (Planet)it.next();
			if(thisPlanet.active){
				activeCount++;
				planetList.add(thisPlanet);
			}
		}
		float oreLeft = addAmount;
		Boolean allAdded = false;
		Boolean allFull = false;
		float addDist = 0;
		if(activeCount > 0){
			addDist = addAmount/((float)activeCount);
		}
		else{
			allAdded = true;
		}		
		while(!allAdded && !allFull){
			allFull = true;
			for(Iterator it2 = planetList.iterator();it2.hasNext();){
				Planet nextPlanet = (Planet)it2.next();
				if(nextPlanet.ore + addDist <= nextPlanet.maxOre){
					nextPlanet.ore += addDist;
					oreLeft -= addDist;
					allFull = false;
				}
				else if(nextPlanet.ore < nextPlanet.maxOre){
					float tempOre = nextPlanet.maxOre - nextPlanet.ore;
					nextPlanet.ore = nextPlanet.maxOre;
					oreLeft -= tempOre;
					allFull = false;
				}
				if(oreLeft <= 0){
					allAdded = true;
					break;
				}
			}
		}
	}
	
	// Removes an amount of fuel from the total system, spreading the cost between all planets
	public static void removeFuel(float removeAmount){
		int activeCount = 0;
		LinkedList<Planet> planetList = new LinkedList<Planet>();
		for(Iterator it = idList.iterator(); it.hasNext();){
			Planet thisPlanet = (Planet)it.next();
			if(thisPlanet.active){
				activeCount++;
				planetList.add(thisPlanet);
			}
		}
		float fuelLeft = removeAmount;
		Boolean allRemoved = false;
		float removeDist = 0;
		if(activeCount > 0){
			removeDist = removeAmount/((float)activeCount);
		}
		else{
			allRemoved = true;
		}
		while(!allRemoved){
			for(Iterator it2 = planetList.iterator();it2.hasNext();){
				Planet nextPlanet = (Planet)it2.next();
				if(nextPlanet.fuel >= removeDist){
					nextPlanet.fuel -= removeDist;
					fuelLeft -= removeDist;
				}
				else if(nextPlanet.fuel > 0){
					float tempFuel = nextPlanet.fuel;
					nextPlanet.fuel = 0;
					fuelLeft -= tempFuel;
				}
				if(fuelLeft <= 0){
					allRemoved = true;
					break;
				}
			}
		}
	}
	
	// Draw the dots from Planets in the top down map
	public static void drawTdIcons(float sf) {
		Graphics2D gh = Game.getGraphicsHandle();
		for(Iterator it = idList.iterator(); it.hasNext();){
			Planet pid = (Planet)it.next();
			if (!Fog.pointIsInside(pid.x, pid.y)) {
				if (pid.disFromEarth == 0) {
					gh.setColor(Color.GREEN);
					gh.fillOval((int)(pid.x*sf)-5, (int)(pid.y*sf)-5, 11, 11);
				} else if (pid.disFromEarth > 0) {
					gh.setColor(Color.CYAN);
					gh.fillOval((int)(pid.x*sf)-3, (int)(pid.y*sf)-3, 7, 7);
				} else if (pid.disFromEarth == -2) {
					gh.setColor(Color.RED);
					gh.fillOval((int)(pid.x*sf)-3, (int)(pid.y*sf)-3, 7, 7);
				} else {
					gh.setColor(Color.DARK_GRAY);
					gh.fillOval((int)(pid.x*sf)-3, (int)(pid.y*sf)-3, 7, 7);
				}
			}
		}
	}
	
	// Finds the shortest graph connecting all planets back to earth,
	//  and returns the distance of this. Returns -1 if any planets
	//  are not able to be connected back to earth.
	public static SimpleWeightedGraph<Planet, DefaultWeightedEdge> getGraph(Planet dniPlanet) {
		// Create graph
		SimpleWeightedGraph<Planet, DefaultWeightedEdge> planetGraph =
				new SimpleWeightedGraph<Planet, DefaultWeightedEdge>(DefaultWeightedEdge.class);
		
		// Copy List
		LinkedList<Planet> pList = new LinkedList<Planet>();
		for(Iterator it = idList.iterator(); it.hasNext();) {
			Planet id = (Planet) it.next();
			if (id != dniPlanet) {
				pList.push(id);
				// Add the node id
				planetGraph.addVertex(id);
			}
		}
		
		// Loop through all planets - generate graph of all possible edges
		while (!pList.isEmpty()) {
			Planet id1 = pList.pop();
			for(Iterator it = pList.iterator(); it.hasNext();) {
				Planet id2 = (Planet) it.next();
				// Do something
				Item lineTest = new Item();
				lineTest.r = 10;
				lineTest.x = id1.x;
				lineTest.y = id1.y;
				lineTest.x2 = id2.x;
				lineTest.y2 = id2.y;
				lineTest.colType = Item.COL_LINE;
				if (Planet.checkCollision(lineTest, false) == 2 && 	Antimatter.checkCollision(lineTest, false) == 0 &&
						AsteroidField.checkCollision(lineTest, false) == 0 && Nebula.checkCollision(lineTest, false) == 0) {
					// Add the edge id1<->id2
					DefaultWeightedEdge tempEdge = planetGraph.addEdge(id1, id2);
					planetGraph.setEdgeWeight(tempEdge, getDistance(id1, id2));
				}
			}
		}
		
		return planetGraph;
	}
	
	// Returns the set of edges for the optimal graph
	public static KruskalMinimumSpanningTree<Planet, DefaultWeightedEdge> getMinTree(SimpleWeightedGraph<Planet, DefaultWeightedEdge> planetGraph){
		KruskalMinimumSpanningTree<Planet, DefaultWeightedEdge> minTree = 
				new KruskalMinimumSpanningTree<Planet, DefaultWeightedEdge>(planetGraph);
		return minTree;
	}
	
	// Returns distance between two planets
	private static long getDistance(Planet p1, Planet p2) {
		int x1 = p1.x;
		int y1 = p1.y;
		int x2 = p2.x;
		int y2 = p2.y;
		return (long)Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
	}
	
	// Returns whether or not the given planet is removable
	private static boolean isPlanetRemovable(Planet thePlanet) {
		if (thePlanet.disFromEarth == 0) {return false;}
		SimpleWeightedGraph<Planet, DefaultWeightedEdge> theGraph = Planet.getGraph(thePlanet);
		ConnectivityInspector conInspector = new ConnectivityInspector(theGraph);
		return conInspector.isGraphConnected();
	}
	
	// Returns a Planet that has been conquered and can be removed from the game without making it impossible to win
	public static Planet getRemovableConqueredPlanet() {
		LinkedList<Planet> pList = new LinkedList<Planet>();
		for(Iterator it = idList.iterator(); it.hasNext();) {
			Planet id = (Planet) it.next();
			if (id.disFromEarth > 0) {
				pList.push(id);
			}
		}
		if (pList.isEmpty()) {return null;}
		
		int nTries = 0;
		do {
			int randIn = Game.randomInt(pList.size());	
			Planet pid = pList.get(randIn);
			if (isPlanetRemovable(pid)) {
				return pid;
			}
		} while (++nTries < 5);
		return null;
	}
	
	public String getName() {return name;}
	public int getDistanceFromEarth(){return this.disFromEarth;}
}
